/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_CONTAINER_CONTAINER_BASE_HPP
#define	_CUBEFS_CORE_CONTAINER_CONTAINER_BASE_HPP

#include <cubefs/core/container/profile.hpp>
#include <cubefs/core/container/builder.hpp>
#include <cubefs/core/container/names.hpp>
#include <cubefs/core/container/type_locator.hpp>
#include <cubefs/core/container/page_mgr.hpp>

#include <cubefs/core/tools/types/typelist.hpp>
#include <cubefs/core/tools/types/typemap.hpp>
#include <cubefs/core/tools/reflection.hpp>

#include <cubefs/api/api.hpp>

#include <string>
#include <iostream>
#include <fstream>
#include <map>

#define CUBEFS_SOURCE "cubefs/core/container/container/base.hpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {

using cubefs::TypeList;
using namespace cubefs::tools;
using namespace cubefs::api;

template <typename Profile> class TypeContainer;

template <typename Profile, typename MyType, typename TypesType>
class ContainerBase: public TypesType::ContainerInterface {
public:

    typedef TypesType                                                           Types;
    typedef typename Types::PageManager                       					PageManagerType;
protected:

    typedef typename TypeLocator<Profile, Root>::Type							RootMap;

    typedef typename PageManagerType::Page                                      PageType;
    typedef typename PageManagerType::Page::ID                                  PageId;

    static ContainerMetadata*                                                   reflection_;
    MyType &me_;

    typedef std::map<PageId, cubefs::api::Model*> CollectionsMap;

    CollectionsMap collections_;

    class MyRootMapInterface: public RootMapInterface<PageId> {
    	RootMap* root_map_;
    public:
    	MyRootMapInterface(): root_map_(NULL) {}
    	virtual ~MyRootMapInterface() {}

    	void set_root_map(RootMap* root_map) {
    		root_map_ = root_map;
    	}

    	virtual void set_root(const PageId& id) {
    		root_map_->set_root(id);
    	}

    	virtual void remove_by_key(BigInt name) {
    		root_map_->RemoveByKey(name);
    	}

    	virtual void set_value_for_key(BigInt name, const PageId& page_id) {
    		root_map_->SetValueForKey(name, page_id);
    	}

    	virtual Int HashCode() {
    		return 0;
    	}
    };

    MyRootMapInterface 	root_map_interface_;
    PageManagerType 	page_manager_;
    RootMap				roots_;

public:

    ContainerBase(MyType &me): me_(me), root_map_interface_(), page_manager_(&me, &root_map_interface_), roots_(page_manager_, 0)
    {
    	root_map_interface_.set_root_map(&roots_);
    }

    virtual ~ContainerBase() throw () {

    }

    virtual RootMapInterfaceBase* GetRootMapInterface() {
    	return &root_map_interface_;
    }

    virtual void* internal_page_mgr() {
    	return &page_manager_;
    }

    static Int hash() {
        return reflection_->Hash();
    }

    static ContainerMetadata* reflection() {
    	return reflection_;
    }

    ContainerMetadata* GetMetadata() {
    	return reflection();
    }

    virtual void ReleaseCollection(cubefs::api::Model* collection)
    {
        if (collection == NULL) throw NullPointerException(CUBEFS_SOURCE, "Collection must not be null");

        TypedIDValue* rootId = collection->GetRootID();

        PageId id(rootId->GetValue());

        typename CollectionsMap::iterator i = collections_.find(id);
        if (i != collections_.end()) {
            collections_.erase(i);
            delete collection;
        }

        delete rootId;
    }

    virtual void RemoveCollection(cubefs::api::Model* collection)
    {
        collection->Drop(&me_);
    }

    virtual cubefs::api::Model* GetCollection(TypedIDValue* rootId)
    {
        if (rootId == NULL) throw NullPointerException(CUBEFS_SOURCE, "Root page ID value must not be null");

        PageId id(rootId->GetValue());

        if (id.is_null()) throw NullPointerException(CUBEFS_SOURCE, "Provided IDValue contains null ID");

        typename CollectionsMap::iterator i = collections_.find(id);

        if (i != collections_.end()) {
            return i->second;
        }
        else {
            ContainerMetadata* metadata = reflection();

            PageType* root = mgr()->get(id);

            if (root == NULL) throw NullPointerException(CUBEFS_SOURCE, "Unknown Page ID");

            ModelMetadata* model = (ModelMetadata*)metadata->GetModelMetadata(root->model_hash());
            ModelFactoryFn factory = model->Factory();
            if (factory == NULL) {
                throw NullPointerException(CUBEFS_SOURCE, "Factory is not specified for this model");
            }

            cubefs::api::Model* c = (*factory)(rootId, &me_, -1);

            collections_[id] = c;

            return c;
        }
    }

    virtual cubefs::api::Model* CreateCollection(Int type, BigInt name)
    {
        if (name <= 0)
        {
            throw CubefsException(CUBEFS_SOURCE, "Model name must be positive");
        }

        ContainerMetadata* metadata = reflection();

        for (Int c = 0; c < metadata->Size(); c++)
        {
            ModelMetadata* model = (ModelMetadata*)metadata->GetItem(c);
            if (model->Code() == type)
            {
                ModelFactoryFn factory = model->Factory();
                if (factory == NULL) {
                    throw NullPointerException(CUBEFS_SOURCE, "Factory is not specified for this model");
                }
                return (*factory)(NULL, &me_, name);
            }
        }

        throw CubefsException(CUBEFS_SOURCE, "Can't create a model of the specified type");
    }

    static Int Init()
    {
        if (reflection_ == NULL) {
            MetadataList list;
            Types::ModelDispatcher::BuildMetadataList(list);
            reflection_ = new ContainerMetadataImpl("CONTAINER", list);
        }
        return reflection_->Hash();
    }

    static void Destroy()
    {
        if (reflection_ != NULL)
        {
        	delete reflection_;
        	Types::ModelDispatcher::DestroyMetadata();
        	reflection_ = NULL;
        }
    }

    static void BuildOperationsList(ListOfOperations &list) {
    }

    RootMap* roots() {
    	return &roots_;
    }

    virtual TreeMap *GetRootMap() {
    	return roots();
    }

    virtual PageManagerType* mgr() {
    	return &page_manager_;
    }

    virtual Manager* GetManager() {
    	return page_manager_.get_manager();
    }

    virtual void SetManager(Manager* manager) {

//    	TypedIDValueImpl id;
//    	manager->GetRootPageId(&id);
//
//    	CUBEFS_TRACE(me_, "SetManager", id.GetValue());

    	if (page_manager_.get_manager() != manager)
    	{
    		page_manager_.set_manager(manager);
    		manager->SetContainer(&me_);
    	}
    }

    virtual cubefs::api::Model* GetCollectionByName(BigInt name)
    {
    	PageId id;
    	if (roots()->GetValue(name, 0, id))
    	{
    		TypedIDValueImpl idValue(&id);
    		return me_.GetCollection(&idValue);
    	}
    	else {
    		return NULL;
    	}
    }
};

template <typename Profile, typename MyType, typename TypesType>
ContainerMetadata* ContainerBase<Profile, MyType, TypesType>::reflection_ = NULL;

}
}

#undef CUBEFS_SOURCE
#endif
